package mapEditor.map;

import debug.*;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Dimension;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JScrollPane;

import mapEditor.MapEditor;
import mapEditor.interactor.EditInteractor;
import mapEditor.interactor.ZoomInteractor;
import mapEditor.interactor.PanInteractor;
import mapEditor.interactor.DrawEllipseInteractor;
import mapEditor.interactor.DrawLineInteractor;
import mapEditor.interactor.MapInteractorAdapter;
import mapEditor.interactor.MeasureInteractor;
import mapEditor.interactor.DrawParallelogramInteractor;
import mapEditor.interactor.DrawPathInteractor;
import mapEditor.interactor.DrawPolygonInteractor;
import mapEditor.interactor.DrawTextInteractor;
import mapEditor.obj.DrawObject;
import mapEditor.svg.SVGDOMTree;
import mapEditor.utils.MapLocation;
import mapEditor.utils.Util;

import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.JSVGScrollPane;
import org.apache.batik.swing.gvt.Interactor;
import org.apache.batik.bridge.UpdateManager;
import org.apache.batik.bridge.UpdateManagerListener;
import org.apache.batik.bridge.UpdateManagerAdapter;
import org.apache.batik.bridge.UpdateManagerEvent;
import org.apache.batik.util.RunnableQueue;


// DOC: http://www.w3.org/TR/2006/WD-DOM-Level-3-Events-20060413/events.html#Events-EventTypes-complete
public class MapCanvas extends JSVGCanvas
{ static final long serialVersionUID=0; // AD'HOC

  private SVGDOMTree domTree;
  public SVGDOMTree getDOMTree() { return(domTree); }
  public String getName() {return((domTree==null)?"UNKNOWN":domTree.getName()); }
  public MapGrid getMapGrid() { return(domTree.getMapGrid()); }

  private JSVGScrollPane scrollPane;
  
  private boolean showMapImage=true;
  private boolean showObjects=true;
  private boolean showGrid=true;
	  
  public void setShowMapImage(boolean on) { showMapImage=on; repaint(); }
  public void showObjectsAction(boolean on) { showObjects=on; repaint(); }
  public void setShowGrid(boolean on) { showGrid=on; repaint(); }
  
  private List interactors;
  private MapInteractorAdapter mapInteractor;
  private DrawObject.Mark[] selectedMarks;
  
  private MapCanvas() {} // NOTE: PRIVATE CONSTRUCTOR

  public static MapCanvas createMapCanvas(String name,URL backgroundImageUrl)	
  { MapCanvas mapCanvas=new MapCanvas();
    mapCanvas.setDocumentState(ALWAYS_DYNAMIC);
    mapCanvas.interactors=mapCanvas.getInteractors();
//    Toolkit toolkit=Toolkit.getDefaultToolkit();
//    Image background=toolkit.getImage(backgroundImageUrl);
//
//    while(background.getWidth(null)<0) // Ensure background image loaded
//    { try { Thread.sleep(100); } catch(InterruptedException ignore){} }	

    // Create Common DOM TREE for MapCanvas and MapViewer
//    MapGrid mapGrid=new MapGrid(background);
    mapCanvas.domTree=new SVGDOMTree(name,mapCanvas,backgroundImageUrl);//,mapGrid);
    name=mapCanvas.domTree.getName();
//    mapCanvas.domTree.setBackgroundImage(backgroundImageUrl.toString(),background.getWidth(null),background.getHeight(null));
    mapCanvas.setDocument(mapCanvas.domTree.getDocument());
    mapCanvas.addUpdateManagerListener(mapCanvas.updateManagerListener);
    mapCanvas.requestResize();
    mapCanvas.setInteractorAdapter(MapInteractorAdapter.DRAW_ELLIPSE);//.MEASURE);
    mapCanvas.setSelected();
	return(mapCanvas);
  }

  public static MapCanvas openMapCanvas(String name,URL svgUrl)
  { MapCanvas mapCanvas=new MapCanvas();
    mapCanvas.setDocumentState(ALWAYS_DYNAMIC);
    mapCanvas.interactors=mapCanvas.getInteractors();
	// Create Common DOM TREE for MapCanvas and MapViewer
    MapGrid mapGrid=new MapGrid();
    mapCanvas.domTree=new SVGDOMTree(name,mapCanvas,svgUrl.toString(),mapGrid); // Read SVGDocument
    name=mapCanvas.domTree.getName();
//	this.setDocument(domTree.getDocument()); // DONE VIA LISTERNER !
    mapCanvas.addUpdateManagerListener(mapCanvas.updateManagerListener);
    mapCanvas.requestResize();
    mapCanvas.setInteractorAdapter(MapInteractorAdapter.MEASURE);
    mapCanvas.setSelected();
	return(mapCanvas);
  }

  
  // ******************************************************************
  // *** METHOD: setInteractorAdapter
  // ******************************************************************
  public void setInteractorAdapter(int mouseMode)
  { MapInteractorAdapter a=null;
    switch(mouseMode)
    { case MapInteractorAdapter.MEASURE:        a=new MeasureInteractor(this);   break;
      case MapInteractorAdapter.EDIT:           a=new EditInteractor(this);      break; 
      case MapInteractorAdapter.ZOOM:           a=new ZoomInteractor(this);      break;
      case MapInteractorAdapter.PAN:            a=new PanInteractor(this);       break;
      case MapInteractorAdapter.DRAW_LINE:      a=new DrawLineInteractor(this);      break;
      case MapInteractorAdapter.DRAW_POLYGON:   a=new DrawPolygonInteractor(this);   break;
      case MapInteractorAdapter.DRAW_ELLIPSE:   a=new DrawEllipseInteractor(this);   break;
      case MapInteractorAdapter.DRAW_PARALLELOGRAM: a=new DrawParallelogramInteractor(this); break;
      case MapInteractorAdapter.DRAW_PATH:      a=new DrawPathInteractor(this);      break;
      case MapInteractorAdapter.DRAW_TEXT:      a=new DrawTextInteractor(this);      break;
    }
    if(mapInteractor!=null)
    { mapInteractor.shutDown();
      this.interactors.remove(mapInteractor);
    }
    this.mapInteractor=a;
    Debug.TRACE("MapCanvas.setInteractorAdapter: add mapInteractor"+mapInteractor);
    this.interactors.add(mapInteractor);    
    MapEditor.getStatusLine().setMode(mapInteractor.getMouseModeMnemonic());
  }
    
  // ******************************************************************
  // *** METHOD: getInteractorAdapter
  // ******************************************************************
  public Interactor getInteractorAdapter() { return(mapInteractor); }

  // ******************************************************************
  // *** METHOD: setSelected
  // ******************************************************************
  public void setSelected(DrawObject selected)
  { selectedMarks=(selected==null)?null:selected.getMarks();
    MapEditor.getSelectedTable().setSelected(selected);
    repaint();
  }

  // ******************************************************************
  // *** METHOD: zoom
  // ******************************************************************
  public void zoom(boolean in)
  {
	  Debug.NOT_IMPLEMENTED("MapCanvas.zoom");
  }
  
  // *************************************************************************
  // *** METHOD: writeSVGFile
  // *************************************************************************
  public void writeSVGFile(Writer writer) throws IOException
  { 
//	String viewBox="\"0 0 "+getMapGrid().getMapWidth()+' '+getMapGrid().getMapHeight()+"\"";
//    StringBuffer svg=new StringBuffer();
//    svg.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
//    svg.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1 Tiny//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd\">\n");
//    svg.append("<svg id=\""+id+"\" version=\"1.1\" baseProfile=\"tiny\"\n");
//    svg.append("     viewBox="+viewBox+"\n");
//    svg.append("     xmlns=\"http://www.w3.org/2000/svg\"\n");
//    svg.append("     xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n");
//    svg.append("  <g id=\"attachedObjects\">\n");
//    for(Enumeration<DrawObject> e=getObjectTableModel().elements() ; e.hasMoreElements() ;)
//    { svg.append("  ").append(e.nextElement().getSVGString()).append('\n'); }
//    svg.append("  </g>\n");
//    svg.append("</svg>\n");

	updateDOMTree();
//	domTree.setViewPortSize((int)domTree.getMapGrid().getMapWidth(),(int)domTree.getMapGrid().getMapHeight());
    domTree.printXML("MapViewer.getSVGFileText.TEST");
    domTree.writeXML(writer);
  }
  
  public void updateInDOMTreeNow(Runnable runnable)
  { UpdateManager updateManager=this.getUpdateManager();
	if(updateManager!=null)
	{ RunnableQueue runnableQueue=updateManager.getUpdateRunnableQueue();
//	  runnableQueue.invokeLater(runnable);
	  try
	  { runnableQueue.invokeAndWait(runnable);
	  } catch(InterruptedException e) { e.printStackTrace(); }
	}
  }
  
  public void updateInDOMTreeLater(Runnable runnable)
  { UpdateManager updateManager=this.getUpdateManager();
	if(updateManager!=null)
	{ RunnableQueue runnableQueue=updateManager.getUpdateRunnableQueue();
	  runnableQueue.invokeLater(runnable);
	}
  }
  
//  public void updateInDOMTreeLater(Runnable runnable,UpdateManagerListener updateManagerListener)
//  { if(updateManagerListener!=null) addUpdateManagerListener(updateManagerListener);
//	UpdateManager updateManager=this.getUpdateManager();
//	if(updateManager!=null)
//	{ RunnableQueue runnableQueue=updateManager.getUpdateRunnableQueue();
//	  runnableQueue.invokeLater(runnable);
//	}
//  }
  
  public void updateDOMTree()
  { updateInDOMTreeNow(new Runnable()
	{ public void run()
	  { try
	    { domTree.getMapGrid().writeGridMetaData(domTree.getMetaDataElement(true));
//	      domTree.setViewBox(0,0,(float)domTree.getMapGrid().getMapWidth(),(float)domTree.getMapGrid().getMapHeight());  
	      for(Enumeration<DrawObject> e=domTree.getObjectTableModel().elements() ; e.hasMoreElements() ;)
	      { e.nextElement().updateSVGElementLater(); }
	    } catch(Throwable e) { e.printStackTrace(); }
	  }
	});
  }
  
  private UpdateManagerListener updateManagerListener=new UpdateManagerAdapter()
  { public void managerStarted(UpdateManagerEvent e)
    { Debug.TRACE("MapCanvas.managerStarted: e="+e); }
    public void managerSuspended(UpdateManagerEvent e)
    { Debug.TRACE("MapCanvas.managerSuspended: e="+e); }
    public void managerResumed(UpdateManagerEvent e)
    { Debug.TRACE("MapCanvas.managerResumed: e="+e); }
    public void managerStopped(UpdateManagerEvent e)
    { Debug.TRACE("MapCanvas.managerStopped: e="+e); }
    public void updateStarted(UpdateManagerEvent e)
    { Debug.TRACE("MapCanvas.updateStarted: e="+e); }
    public void updateCompleted(UpdateManagerEvent e)
    { Debug.TRACE("MapCanvas.updateCompleted: e="+e);
//      requestResize();
    }
    public void updateFailed(UpdateManagerEvent e)
    { Debug.TRACE("MapCanvas.updateFailed: e="+e); }
	  
  };

  public void setScrollPane(JSVGScrollPane scrollPane)
  {	Debug.TRACE("MapCanvas.setScrollPane: "+scrollPane);
	this.scrollPane=scrollPane;
  }
  
  public void requestResize()
  {	int newWidth=(int)(domTree.getMapGrid().getMapWidth());
    int newHeight=(int)(domTree.getMapGrid().getMapHeight());
	Debug.TRACE("MapCanvas.requestResize: new Width="+newWidth+", new Height="+newHeight);
	setPreferredSize(new Dimension(newWidth,newHeight));
    if(scrollPane!=null) scrollPane.reset();
  }
  
  public DrawObject getObject(MapLocation p)
  { ObjectTableModel objectTableModel=domTree.getObjectTableModel();
	for(Enumeration<DrawObject> e=objectTableModel.elements();e.hasMoreElements();)
	{ DrawObject obj=e.nextElement();
	  if(obj.contains(p)) return(obj);
	}
	return(null);
  }
  
  public DrawObject.Mark getMark(MapLocation p)
  { if(selectedMarks==null) return(null);
    if(selectedMarks.length==0) return(null);
    double x=p.getX();
    double y=p.getY();
	for(int i=0;i<selectedMarks.length;i++)
    { double xL=selectedMarks[i].getMapLocation().getX()-5;
      double xH=xL+10;//marks[i].getX()+5;
      double yL=selectedMarks[i].getMapLocation().getY()-5;
      double yH=yL+10;//marks[i].getY()+5;
//      boolean inXrange=( xL<=x ) && ( x<=xH);
//      Debug.TRACE("Check Mark "+i+": xL="+xL+", x="+x+" ,xH="+xH+", inXrange="+inXrange);
	  if( ( xL<=x ) && ( x<=xH) && ( yL<=y ) && ( y<=yH) ) return(selectedMarks[i]);
    }
	return(null);  
  }

  public void measurementDone(MapLocation loc1,MapLocation loc2)
  { double dist=Util.round(loc1.getDistanceInMeter(loc2),100);
    if(dist>0.1) JOptionPane.showMessageDialog(null,"DISTANCE: "+dist+" meter");
  }

  public void drawObjectDone(DrawObject obj,String id)
  { int result=JOptionPane.showConfirmDialog(null,"DRAW "+id+": DONE\n Edit Attributes ?"
      		  ,"Add "+obj.getClassName(),JOptionPane.YES_NO_CANCEL_OPTION);
    if((result==JOptionPane.YES_OPTION) || (result==JOptionPane.NO_OPTION))
	{ domTree.getObjectTableModel().add(obj);
//    tablePane.updateTable();
      domTree.getTablePane().updateTable();
      if(result==JOptionPane.YES_OPTION) obj.edit();
    }
    else repaint();
  }

  // *************************************************************************
  // *** METHOD: setSelected
  // *************************************************************************
  /**
  * Set this MapCanvas as selected among the tabed ones.
  **/
  public void setSelected()
  { int mouseMode=mapInteractor.getMouseMode();
    MapEditor.getControlPanel().setMouseMode(mouseMode);
//    String mouseModeMnemonic=MapMouseAdapter.MouseModeMnemonic[mouseMode];
    String mouseModeMnemonic=mapInteractor.getMouseModeMnemonic();
    MapEditor.getStatusLine().setMode(mouseModeMnemonic);
  }

  // *************************************************************************
  // *** METHOD: print
  // *************************************************************************
  public void print()
  { Debug.NOT_IMPLEMENTED("MapViewer.print");
    domTree.print();
  }

  // *************************************************************************
  // *** METHOD: paint
  // *************************************************************************
  public void paint(Graphics g)
  { //if(showMapImage) domTree.getMapGrid().paintMap(g,this);
    if(showObjects)
    { //for(Enumeration e=domTree.getObjectTableModel().elements() ; e.hasMoreElements() ;)
//      { DrawObject obj=(DrawObject)(e.nextElement());
//        obj.paint((Graphics2D)g);
//      }
      super.paint(g);
    }
//    if(interactor instanceof MapInteractorAdapter) ((MapInteractorAdapter)interactor).paintMouseEffect((Graphics2D)g); 
    mapInteractor.paintMouseEffect((Graphics2D)g); 
	if(showGrid) domTree.getMapGrid().paintGrid((Graphics2D)g);
    if(selectedMarks!=null)
  	{ for(int i=0;i<selectedMarks.length;i++)
  	  selectedMarks[i].paint((Graphics2D)g);
  	}
  }

}
